gusucode.com > DirTree 树形菜单资源管理器-源码程序 > DirTree 树形菜单资源管理器/DirTree/DirTreeView.cpp

    // DirTreeView.cpp : implementation of the CDirTreeView class

#include "stdafx.h"
#include "DirTree.h"

#include "DirTreeDoc.h"
#include "DirTreeView.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

/////////////////////////////////////////////////////////////////////////////
// CDirTreeView

IMPLEMENT_DYNCREATE(CDirTreeView, CTreeView)

BEGIN_MESSAGE_MAP(CDirTreeView, CTreeView)
	//{{AFX_MSG_MAP(CDirTreeView)
	ON_WM_DESTROY()
	ON_NOTIFY_REFLECT(TVN_ITEMEXPANDED, OnItemexpanded)
	ON_NOTIFY_REFLECT(TVN_SELCHANGED, OnSelchanged)
	//}}AFX_MSG_MAP
	// Standard printing commands
	ON_COMMAND(ID_FILE_PRINT, CTreeView::OnFilePrint)
	ON_COMMAND(ID_FILE_PRINT_DIRECT, CTreeView::OnFilePrint)
	ON_COMMAND(ID_FILE_PRINT_PREVIEW, CTreeView::OnFilePrintPreview)
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CDirTreeView construction/destruction

CDirTreeView::CDirTreeView()
{
	m_nLevel = 0;
}

CDirTreeView::~CDirTreeView()
{
}

BOOL CDirTreeView::PreCreateWindow(CREATESTRUCT& cs)
{
	// TODO: Modify the Window class or styles here by modifying
	//  the CREATESTRUCT cs
	cs.style |= TVS_HASBUTTONS | TVS_HASLINES | TVS_LINESATROOT | TVS_DISABLEDRAGDROP | TVS_SHOWSELALWAYS;
	return CTreeView::PreCreateWindow(cs);
}

/////////////////////////////////////////////////////////////////////////////
// CDirTreeView drawing

void CDirTreeView::OnDraw(CDC* pDC)
{
	CDirTreeDoc* pDoc = GetDocument();
	ASSERT_VALID(pDoc);
	// TODO: add draw code for native data here
}

void CDirTreeView::OnInitialUpdate()
{
	CTreeCtrl&	tc = GetTreeCtrl ();
	CTreeView::OnInitialUpdate();

	LoadSystemImageList (SHGFI_SMALLICON);
	tc.SetImageList (&m_Images, TVSIL_NORMAL);
	InitializeTree ();
	SetDirectoryName (GetCurrentDirectory (), true);
}

/////////////////////////////////////////////////////////////////////////////
// CDirTreeView printing

BOOL CDirTreeView::OnPreparePrinting(CPrintInfo* pInfo)
{
	// default preparation
	return DoPreparePrinting(pInfo);
}

void CDirTreeView::OnBeginPrinting(CDC* /*pDC*/, CPrintInfo* /*pInfo*/)
{
	// TODO: add extra initialization before printing
}

void CDirTreeView::OnEndPrinting(CDC* /*pDC*/, CPrintInfo* /*pInfo*/)
{
	// TODO: add cleanup after printing
}

/////////////////////////////////////////////////////////////////////////////
// CDirTreeView diagnostics

#ifdef _DEBUG
void CDirTreeView::AssertValid() const
{
	CTreeView::AssertValid();
}

void CDirTreeView::Dump(CDumpContext& dc) const
{
	CTreeView::Dump(dc);
}

CDirTreeDoc* CDirTreeView::GetDocument() // non-debug version is inline
{
	ASSERT(m_pDocument->IsKindOf(RUNTIME_CLASS(CDirTreeDoc)));
	return (CDirTreeDoc*)m_pDocument;
}
#endif //_DEBUG

/////////////////////////////////////////////////////////////////////////////
// CDirTreeView message handlers

void CDirTreeView::SetDirectoryName(CString & dir, bool bDisplay)
{
	if (!strcmp ((LPCSTR) dir, ".") || dir.IsEmpty())
	{
		m_StartPath = GetCurrentDirectory ();
	}
	else
	{
		m_StartPath = dir;
	}
	if (bDisplay)
	{
		if (m_StartPath.GetLength())
			SelectStartDir ((char *) (LPCSTR)m_StartPath);
	}
}

void CDirTreeView::OnItemexpanded(NMHDR* pNMHDR, LRESULT* pResult) 
{
	NM_TREEVIEW* pNMTreeView = (NM_TREEVIEW*)pNMHDR;

	CTreeCtrl&	tc = GetTreeCtrl ();
// get the node that was expanded or contracted
	HTREEITEM node = pNMTreeView->itemNew.hItem;

//	Get the Tree Item for the node. Zero out the new structure. Tree control
//	functions don't like stray data.
	TV_ITEM item;
	memcpy ((char *) &item, (char *) &pNMTreeView->itemNew, sizeof (TV_ITEM));

//	if the node was contracted then set the item data so that
//	later we know not to rebuild the node.
	if (pNMTreeView->action == TVE_COLLAPSE)
	{
		item.mask = TVIF_PARAM;
		item.lParam = CDirTreeView::tmExpanded;
		tc.SetItem(&item);
		return;
	}

// If the node was already built once then we don't need to do
// it again. If we do, we'll have duplicate entries
	if (item.lParam == CDirTreeView::tmExpanded)
		return;

// remove the first child because we're going to add it again
// remember that we have to have already added one child per
// node so that the plus sign gets added to the node
	HTREEITEM child = tc.GetChildItem(node);
	if (child)
		tc.DeleteItem(child);

// add this node and all subnodes
	AddNode(0, node, 0, CDirTreeView::tmDetail);

	*pResult = 0;
}

void CDirTreeView::OnSelchanged(NMHDR* pNMHDR, LRESULT* pResult) 
{
	NM_TREEVIEW* pNMTreeView = (NM_TREEVIEW*)pNMHDR;

	CTreeCtrl&	tc = GetTreeCtrl ();
	CWaitCursor waitcursor;

//	get the selected node
	HTREEITEM sel = tc.GetSelectedItem();
	CString s;
//	build the path name based on the selection
	BuildPath(s, sel);

//	m_List->ListDirectory (s);

	*pResult = 0;
}

void CDirTreeView::InitializeTree ()
{
	LoadTree ();
	if (m_StartPath.GetLength())
		SelectStartDir ((char *) (LPCSTR)m_StartPath);
}

void CDirTreeView::LoadTree()
{
char			drives [_MAX_PATH];
char *aDrive, *s;	//	= drives;
CTreeCtrl&		tc = GetTreeCtrl ();
HTREEITEM		RootNode = tc.GetRootItem ();

//	Get the drive string. This is a double NUL terminated list, with
//	each item terminated with a NUL.
//
	if (::GetLogicalDriveStrings(_MAX_PATH, drives))
	{
	char	CurDrive[64];		//	We need the copy because we are going to
								//	remove any trailing backslash, which would
								//	screw up our double NUL terminated list.
		aDrive = drives;
//	Parse the drives
		do
		{
		UINT	type;
//	Get a drive
			strcpy (CurDrive, aDrive);
			type = ::GetDriveType(CurDrive);

			if ((s = strchr (CurDrive, '\\')) != NULL)
				*s = '\0';
//	Make sure the drive letter is caps and add the node for the drive.
			strupr (CurDrive);
			AddNode(CurDrive, RootNode, type, CDirTreeView::tmShort);
				aDrive += strlen (aDrive) + 1;
		} while(strlen (aDrive));
	}
}

void CDirTreeView::RefreshSelection()
{
CString		strSelected;
CTreeCtrl&	tc = GetTreeCtrl ();
UINT		state;

//	Pop up a wait cursor
	CWaitCursor waitcursor;
//	get the selected node
	HTREEITEM sel = tc.GetSelectedItem();
	HTREEITEM parent = tc.GetParentItem(sel);
	state = tc.GetItemState (parent, TVIF_STATE);
	strSelected = tc.GetItemText (sel);
	tc.DeleteItem (sel);
	AddNode ((LPCSTR) strSelected, parent, 0, CDirTreeView::tmShort);
	BuildPath (strSelected, parent);
	SelectStartDir ((char *) (LPCSTR)strSelected);
	m_StartPath = strSelected;
	sel = tc.GetSelectedItem();
	if (state & TVIS_EXPANDED)
		tc.Expand (sel, TVE_EXPAND);		// Always expand
}

void CDirTreeView::DeleteNode(CString & NodeName)
{

}

// AddNode(). This function adds the nodes to the tree. The first
// time we display a node we don't want to add each and every
// sub-directory to the tree. We only want to add nodes when
// necessary or it will take forever for the dialog box to come
// up initially. But, we have to add at least one node so that
// the + sign shows up indicating that sub-directories exist.
void CDirTreeView::AddNode(const char * path, HTREEITEM node, int type, int mode)
{
WIN32_FIND_DATA fd;
HANDLE			hFind;
char			buff [_MAX_PATH];	//	temporary storage
HTREEITEM		newNode = node;		//	May be used to build pathname without
int				image1, image2;										//	adding any new nodes.
CString			dirPath;

//	If a node name was passed to us then we will add the node.
//	Otherwise, we need to get a directory name first and then recurse.
//
	if (path)
	{

//	if the mode indicates that we are adding a drive node
//	then we need to:
//		add the drive label in parens.
//		reset the mode to tmShort (actually, on fast machines this
//			doesn't really speed things up).
		if (type >= DRIVE_REMOVABLE)
		{
		char	VolName[24];
		char	RootName[10];
		DWORD	dwCompLen, dwFlags;

			memset (&fd, '\0', sizeof (WIN32_FIND_DATA));
			fd.dwFileAttributes = FILE_ATTRIBUTE_SYSTEM;
			sprintf (RootName, "%s\\", path);
			memset (VolName, '\0', sizeof (VolName));
			strcpy (fd.cFileName, RootName);
			GetVolumeInformation (RootName, VolName, 24, NULL, &dwCompLen, &dwFlags, NULL, 0);
			if (strlen (VolName))
			{
				wsprintf(buff, "%s (%s)", path, VolName);
			}
			else
				wsprintf (buff, "%s", path);
			image1 = image2 = GetIconIndex (fd);
			newNode = InsertChild (node, buff, image1, image2, TVI_LAST);
			mode = CDirTreeView::tmShort;
		}
//	Otherwise just use the node name passed
		else
		{
			strcpy(buff, path);

//	Add the node as a child to the current node using the folder images.
//	Use the sort flag to sort the tree as we go.so the list is sorted
//	as we go.
			memset (&fd, '\0', sizeof (WIN32_FIND_DATA));
			strcpy (fd.cFileName, buff);
			fd.dwFileAttributes = FILE_ATTRIBUTE_DIRECTORY;
			image1 = GetIconIndex (fd);
			fd.dwFileAttributes = FILE_ATTRIBUTE_DIRECTORY | SHGFI_OPENICON;
			image2 = GetIconIndex (fd);
			newNode = InsertChild (node, buff, image1, image2, TVI_SORT);
		}
	}

//	Build a path name based on the node.
	BuildPath(dirPath, newNode);
//	Add wildcards
	dirPath += "*.*";
//
//	Look for the first match. Return if none.
	if ((hFind = FindFirstFile(dirPath, &fd)) == INVALID_HANDLE_VALUE)
	{
		return;
	}
// add one to the level we are on
	m_nLevel++;
	do
	{
		if ((fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
				&& (!(fd.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN)))
		{
			if (!strcmp (fd.cFileName, "."))
			{
				;		// Current directory. Do nothing.
			}
			else if (!strcmp (fd.cFileName, ".."))
			{
				;		// Parent directory. Do nothing.
			}
			else
			{

//	If the are building the intial tree structure (tmShort) or we are
//	expanding a level (tmDetail), add the node by recursion.
				if (((mode == CDirTreeView::tmShort)
						&& (m_nLevel < 2))
						|| (mode == CDirTreeView::tmDetail))
					AddNode(fd.cFileName, newNode, 0, mode);	// type, 0);	// mode);

//	If we're building the initial structure, we want to add only one
//	subnode to make the plus symbold appear.
				if (mode == CDirTreeView::tmShort)
					break;

//	In the detail mode we need to fill this branch completely
//	but only one sub-node per node under this branch. Again,
//	we have to do this so the + sign shows up next to the node.
				if (mode == CDirTreeView::tmDetail && m_nLevel > 1)
				{
					break;
				}
			}
		}
	} while (::FindNextFile (hFind, &fd));		// Look for the next match
	FindClose (hFind);

// decrement the level counter
	m_nLevel--;
}

//
//	InsertChild() inserts a new node in the tree as a child of the node
//	passed as the parent.Images default to 0 and SortType default to TVI_SORT
//
HTREEITEM CDirTreeView::InsertChild(HTREEITEM parent, char * label, int image, int selimage, HTREEITEM SortType)
{
CTreeCtrl&		tc = GetTreeCtrl ();
TV_INSERTSTRUCT	TreeItem;

//
//	Zero out the new structure. CTreeCtrl doesn't like stray data.
//
	memset ((char *) &TreeItem, '\0', sizeof (TV_INSERTSTRUCT));
	TreeItem.hParent = parent == NULL ? TVI_ROOT : parent;
	TreeItem.hInsertAfter = SortType;
	TreeItem.item.mask = TVIF_TEXT | TVIF_PARAM | TVIF_IMAGE | TVIF_SELECTEDIMAGE;
	TreeItem.item.pszText = label;
	TreeItem.item.cchTextMax = strlen (label) + 1;
	TreeItem.item.lParam = 0;
	TreeItem.item.iImage = image;
	TreeItem.item.iSelectedImage = image;
	TreeItem.item.iSelectedImage = selimage;
	return (tc.InsertItem(&TreeItem));
}

void CDirTreeView::SelectStartDir(char * StartDir)
{
char	*startDir, Drive[32];
char	*s;
CTreeCtrl&	tc = GetTreeCtrl ();
HTREEITEM	RootNode, node, SelectedNode;

	if ((StartDir == NULL) || !strlen (StartDir))
		return;
//
//	Make a copy of the directory string.
//
	startDir = new char [strlen (StartDir) + 1];
	strcpy (startDir, StartDir);
//
//	Make sure the path contains no forward slashes.
//
	while ((s = strchr (startDir, '/')) != NULL)
		*s = '\\';

	s = strtok (startDir, "\\");
	if (s == NULL)
	{
		s = startDir;
	}
//
//	Make it all upper case. Less confusion that way. Our first node
//	should be the device. Append the trailing backslash, which is the
//	way the node is returned from BuildPath
//
//
	strupr (s);
	sprintf (Drive, "%s\\", s);

//	Get the root node
	RootNode = tc.GetRootItem ();
//	Get the first child item of the root node
	node = tc.GetNextItem(RootNode, TVGN_ROOT);
//
//	Loop through the siblings until we find our drive
//
	for (; node; node = tc.GetNextItem(node, TVGN_NEXT))
	{
		CString ItemText;
		BuildPath (ItemText, node);
//
//	If the root paths match, we've found it
//
		if (!strcmpi (Drive, (LPCSTR) ItemText))
			break;
	}
//
//	Did we find the start for our directory?
//
	if (node == NULL)
	{
		delete [] startDir;
		return;
	}
//
//	Select this item just in case the next strtok is NULL
//
	SelectedNode = node;
	s = strtok (NULL, "\\");			// Get the next part of the path
	if (s == NULL)						// If none, we are finished.
	{
		tc.Select (SelectedNode, TVGN_CARET);
		delete [] startDir;
		return;
	}
//
//	There's more to look for, so expand this item.
//
	tc.Expand (node, TVE_EXPAND);		// Always expand
	while (s != NULL)
	{
//		for (node = tc.GetNextItem(node, TVGN_CHILD);
		for (node = tc.GetNextItem(SelectedNode, TVGN_CHILD);
			 node;
			 node = tc.GetNextItem(node, TVGN_NEXT))
		{
			CString ItemText = tc.GetItemText (node);
			if (!ItemText.CompareNoCase (s))
				break;
		}
//
//	If the node was not found, we can't continue. Leave the current
//	selection as it is.
//
        if (node == NULL)
			break;
//
//	Select the current item.
//
		SelectedNode = node;
		s = strtok (NULL, "\\");
//	If the next token is not null, expand the current item.
		if (s != NULL)
			tc.Expand (node, TVE_EXPAND);		// Always expand
	}
	tc.Select (SelectedNode, TVGN_CARET);
	delete [] startDir;
}

//	BuildPath() builds the path name by recursing backward
//	through the tree.
//
void CDirTreeView::BuildPath(CString & path, HTREEITEM node)
{
CTreeCtrl&	tc = GetTreeCtrl ();

// get the parent of the current node
	HTREEITEM parent = tc.GetParentItem(node);

//	Get the text of the node. We'll use it to build the path
	CString buff = tc.GetItemText (node);

// add the backslash to the node
	buff += "\\";

//	Add the current directory name to the path.
//
	buff += path;
	path = buff;

//	If this node has a parent then we recurse until no parent is found.
//	If there is no parent, then we are finished building the path.
//
	if (parent)
	{
		BuildPath(path, parent);
	}
	else
	{
//	If the parent is found then we need to strip the volume name
//	from the string. It is enclosed in parentheses, so look for the
//	first open paren and pick up the left side, then find the first
//	close paren and pick up the right part.
//
		CString dPath = path;
		int		Length = path.GetLength ();
		int pos = dPath.Find("(");
		if (pos > 0)
			path = dPath.Left (pos - 1);
		pos = dPath.Find (")");
		if (pos > 0)
			path += dPath.Right (Length - pos - 1);
	}
}

void CDirTreeView::OnDestroy()
{
	m_Images.Detach();
	CTreeView::OnDestroy ();
}

int CDirTreeView::GetIconIndex (WIN32_FIND_DATA fd)
{
	SHFILEINFO sfi;
	memset(&sfi, 0, sizeof(sfi));
	CString strFilePath = fd.cFileName;
	if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
	{
		SHGetFileInfo (
			strFilePath, 
			FILE_ATTRIBUTE_DIRECTORY, 
			&sfi, 
			sizeof(sfi), 
			SHGFI_SMALLICON |
				SHGFI_SYSICONINDEX | 
				SHGFI_USEFILEATTRIBUTES |
				(fd.dwFileAttributes & SHGFI_OPENICON)
			);
		return (sfi.iIcon);
	}
	else
	{
		SHGetFileInfo (
			strFilePath, 
			FILE_ATTRIBUTE_NORMAL,
//			fd.dwFileAttributes,
			&sfi, 
			sizeof(sfi), 
//			SHGFI_SMALLICON | SHGFI_SYSICONINDEX | SHGFI_USEFILEATTRIBUTES | (fd.dwFileAttributes & SHGFI_OPENICON)
			SHGFI_SMALLICON | SHGFI_SYSICONINDEX | (fd.dwFileAttributes & SHGFI_OPENICON)
		);
		return sfi.iIcon;
	}
	return (-1);
}

BOOL CDirTreeView::LoadSystemImageList(UINT uiImageList)
{
	SHFILEINFO sfiInfo;
	memset(&sfiInfo, 0, sizeof(SHFILEINFO));
	HIMAGELIST hImages = (HIMAGELIST) (
		SHGetFileInfo (
			"C:\\", 
			0, 
			&sfiInfo,
			sizeof(sfiInfo), 
			SHGFI_SYSICONINDEX | uiImageList)
	);
	if (hImages == NULL)
		return (FALSE);
	switch (uiImageList)
	{
		case SHGFI_SMALLICON:
			if (m_Images.m_hImageList != NULL)
				m_Images.Detach ();
			m_Images.Attach (hImages);
			break;
		default:
			return (FALSE);
	}
	return (TRUE);
}

CString & CDirTreeView::GetCurrentDirectory()
{
static CString strCurrentPath;

	strCurrentPath.Empty ();
	int nBytes = ::GetCurrentDirectory (0, NULL);
	char *szDir = new char [nBytes + 1];
	::GetCurrentDirectory (nBytes + 1, szDir);
	strCurrentPath = szDir;
	delete [] szDir;
	return (strCurrentPath);
}